package game;

import heroes.Hero;

import java.rmi.UnexpectedException;
import java.util.ArrayList;

import cards.Armor;
import cards.Basic;
import cards.Card;
import cards.HorseMinus;
import cards.HorsePlus;
import cards.TieSuo;
import cards.Weapon;

public abstract class Model {
	Client myClient;
	Server myServer;
	/*
	 * IMPORTANT the View will talk to his Model, no matter it's a ServerModel
	 * or a ClientModel, the View will call some general method but ServerModel
	 * and ClientModel will handle this differently and ServerModel has more
	 * methods about stack management and condition tests
	 */
	LoginView myLoginView;
	View myView;
	String myPlayerName;

	// the number of the rest cards in the stack
	int cardNum;
	int discardedNum;
	// there need a way to show deck card. Or we don't need to ? TODO

	// this player list will decide the sequence of playing cards, this player
	// list will help to show this sequence in the View panel.
	// UPDATE also keep track of players' handcards. (Only server, client only
	// keeps track of his own hand cards)
	ArrayList<Hero> heroList;
	ArrayList<Player> playerList;
	// check whether if this is my round.
	Player currentPlayer;
	int currentNum;
	Player myPlayer;
	Hero myHero;
	int totalNum;

	protected String findHeroFromUserName(String userName) {
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(userName)) {
				return every.getCharacter().getName();
			}
		}
		return null;
	}

	protected int gameMode = 0;

	/**
	 * currently supporting two game mode: 1. Dynasty mode, 5 or 8 players(Zhu,
	 * Zhong, Fan, Nei); 2. Duel Mode, 2 players();
	 */
	public void initWinningCondition() {
		if (totalNum == 5 || totalNum == 8) {
			gameMode = 1;
		} else if (totalNum == 2) {
			gameMode = 2;
		}
	}

	public void registerPlayers(Player toBregistered) {
		playerList.add(toBregistered);
		totalNum += 1;
		// if(toBregistered.getName() == myPlayerName){
		// myPlayer = toBregistered;
		// }
		// System.out.println("Player registered: "+ toBregistered);
		// System.out.println("TotalNum now is: "+ totalNum);
	}

	public void removePlayer(String userName) {
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(userName)) {
				playerList.remove(every);
				totalNum--;
				return;
			}
		}
	}

	// @deprecated because View.requiredFromOutside help playerRoundChange() to
	// implement the needed features here

	// IMPORTANT this is different from playerRoundChange() as it is about
	// passive changing turn, like NanMan WanJian
	// public void playerTakeTurn() {
	//
	// }

	public void playerRoundChange() throws InterruptedException {
		myView.playerRoundChange();
		// the responsible player's view will do his own job to prepare
		// intoMyRound(). server don't need to worry
		if (currentPlayer == myPlayer) {
			myView.intoMyRound();
		} else {
			System.out.println("currentPlayer != myPlayer:");
			System.out.println("currentPlayer " + currentPlayer);
			System.out.println("myPlayer " + myPlayer);
		}
	}

	// protected
	protected void init() {
		initAllLocks();
	}

	public void initHeroes() {

	}

	public Lock drawCardLock;

	public abstract void drawCard(int i) throws InterruptedException;

	public Lock signalPlayStageLock;

	public abstract void signalPlayStageStart();

	// IMPORTANT, this method is actually included within watchPlay()
	public void receiveSignalPlayStage() {
		signalPlayStageLock.unlock();
	}

	public abstract void signalPlayStageEnd();

	// public void signalDiscardStageStart() {
	// sendLine("G.DISCARDSTAGESTART");
	// }
	public Lock signalDiscardStageLock;

	public abstract void signalDiscardStageStart();

	public void receiveSignalDiscardStage() {
		signalDiscardStageLock.unlock();
	}

	// Lock ciXiongLock;

	public abstract void ciXiong(String targetName);

	public void watchCiXiong(String source, String target) {
		myView.recSysMsg(source + " uses CiXiong on " + target + ".");
		if (myPlayerName.equalsIgnoreCase(target)) {
			myView.ciXiongResponseProcessInit();
		}
		myView.addProgressTo(target);
	}

	// Lock ciXiongResponseLock;

	public abstract void ciXiongResponse(Card theCard);

	public void watchCiXiongResponse(boolean moPai, String userName,
			String cardName, String cardColor, String cardInt) {
		myView.recSysMsg("CiXiong takes effect:");
		Card theCard = Card.createCard(cardName, Integer.parseInt(cardColor),
				Integer.parseInt(cardInt));
		if (moPai) {
			myView.recSysMsg(userName + " draws one card.");

			if (myPlayerName.equalsIgnoreCase(userName)) {
				myHero.getCard(theCard);
				myView.unlockWeaponSkill();
			} else {
				whoDrawNumCard(findHeroFromUserName(userName), 1);
			}
		} else {
			myView.recSysMsg(userName + " discards one card");
			if (myPlayerName.equalsIgnoreCase(userName)) {
				myHero.loseCard(theCard);
			} else {
				whoLoseNumCard(findHeroFromUserName(userName), 1);
			}
		}
	}

	/**
	 * Target discarded one card. Source will not draw a new card.
	 */
	public void watchCiXiongResponse(boolean b, String userName) {
		if (b == false) {
			if (myPlayerName.equalsIgnoreCase(userName)) {
				myView.unlockWeaponSkill();
			}
		}
	}

	// Lock attackLock;

	public abstract void attack(String targetName, Damage damage,
			String cardInfo);

	// will be overridden differently according to server/client
	// protected abstract void sendLine(String toBsend);

	public void whoDrawNumCard(String userName, int num) {
		// update deck info
		cardNum -= num;
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(userName)
					&& !every.getName().equalsIgnoreCase(myPlayerName)) {
				every.getCharacter().handCardNum += num;
				// System.out.println("handCardNum "+
				// every.getCharacter().handCardNum);
				// System.out.println("num" + num);
				break;
			}
		}
		myView.repaint();
	}

	public void whoLoseNumCard(String heroName, int num) {
		for (Hero every : heroList) {
			if (every.getName().equalsIgnoreCase(heroName)) {
				every.handCardNum -= num;
				every.myDisplayer.repaint();
				break;
			}
		}
	}

	public void whoGetCard(String msg) {
		String str[] = msg.split("\\.");
		String tobprint = "";
		int cardNum = Integer.parseInt(str[3]);
		tobprint += (str[2] + " gets " + (cardNum == 1 ? " 1 card" : " "
				+ cardNum + " cards"));
		boolean youGetCard = false;
		boolean seeable = false;
		if (str[4].equals("TRUE")) {
			seeable = true;
		}
		if (str[2].equalsIgnoreCase(myPlayerName)) {
			youGetCard = true;
		}
		for (int i = 0; i < Integer.parseInt(str[3]); i++) {
			if (youGetCard) {
				myHero.getCard(Card.createCard(str[5 + i * 3],
						Integer.parseInt(str[6 + i * 3]),
						Integer.parseInt(str[7 + i * 3])));
			}
			if (seeable) {
				processCardInfo(str[5 + i * 3], str[6 + i * 3], str[7 + i * 3]);
				tobprint += ": " + cardInfo[0] + " " + cardInfo[1]
						+ cardInfo[2];
			}
		}
		// get target hero
		if (!youGetCard) {
			Hero targetHero = null;
			for (Player every : playerList) {
				if (every.getName().equalsIgnoreCase(str[2])) {
					targetHero = every.getCharacter();
				}
			}
			targetHero.handCardNum += cardNum;
		}

		myView.recSysMsg(tobprint);
	}

	// @deprecated , use whoGetCard() instead.
	// public void whoGetNumCard(String heroName, int num) {
	// // give out a information in the RightPanel
	// myView.recSysMsg(heroName + " gets "
	// + (num == 1 ? "1 card." : num + " cards."));
	//
	// // update the battle field look
	// for (Hero every : heroList) {
	// if (every.getName().equalsIgnoreCase(heroName)) {
	// every.handCardNum += num;
	// every.myDisplayer.repaint();
	// break;
	// }
	// }
	// }
	public Lock pandinLock;

	public abstract void PandinCheck(String line) throws InterruptedException;

	public abstract void changePandin(Card toB);

	// 注意这里是3不是2
	String cardInfo[] = new String[3];

	protected void processCardInfo(String cardName, String color, String number) {
		cardInfo[0] = cardName;
		if (color.equalsIgnoreCase("1")) {
			cardInfo[1] = "♥";
		} else if (color.equalsIgnoreCase("2")) {
			cardInfo[1] = "♦";
		} else if (color.equalsIgnoreCase("3")) {
			cardInfo[1] = "♠";
		} else if (color.equalsIgnoreCase("4")) {
			cardInfo[1] = "♣";
		}
		cardInfo[2] = number;
	}

	private void removePandin(String userName, String pandin) {
		Hero targetHero = null;
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(userName)) {
				targetHero = every.getCharacter();
				break;
			}
		}
		if (pandin.equalsIgnoreCase("LEBU")) {
			targetHero.ifLeBu = false;
			targetHero.LeBuCard = null;
		} else if (pandin.equalsIgnoreCase("BINLIANG")) {
			targetHero.ifBinLiang = false;
			targetHero.BinLiangCard = null;
		} else if (pandin.equalsIgnoreCase("SHANDIAN")) {
			targetHero.ifShanDian = false;
			targetHero.ShanDianCard = null;
		}
	}

	// Pandin msg. length == 8 from server,
	// this method also works for server
	public void watchPandin(String msg) throws UnexpectedException {
		String[] str = msg.split("\\.");
		removePandin(str[2], str[3]);
		// if length is 5, it means it's WuXied
		if (str.length == 5 && str[4].equalsIgnoreCase("false")) {
			myView.recSysMsg(str[2] + "'s " + str[3]
					+ " pandin is WuXied. It won't take effect.");
			if (str[2].equalsIgnoreCase(myPlayerName)) {
				if (pandinLock.isLocked) {
					pandinLock.unlock();
				} else {
					// pandinLock.isLocked == false, which should never happen.
					throw new UnexpectedException(
							"pandinLock is unlocked before pandin is resolved.");
				}

			}
			return;
		}
		// "G.PANDIN." + this.userName + "." + pandinName + "."
		// + pandinInfo + "."+effect +"." + myModel.pandinChangePossible
		processCardInfo(str[4], str[5], str[6]);
		this.myView.recSysMsg(str[2] + "'s " + str[3] + " pandin is"
				+ cardInfo[0] + " " + cardInfo[1] + " " + cardInfo[2]);
		if (str[8].equalsIgnoreCase("false")) {
			if (str[7].equalsIgnoreCase("true")) {
				myView.recSysMsg("The magic takes effect.");
				if (str[2].equalsIgnoreCase(myPlayerName)) {
					if (str[3].equalsIgnoreCase("LeBu")) {
						myHero.canPlayCard = false;
					} else if (str[3].equalsIgnoreCase("BinLiang")) {
						myHero.canDrawCard = false;
					} else if (str[3].equalsIgnoreCase("ShanDian")) {
						// 031::
						// server will take care of damage process.
					}
					// wait for the pandin action end.
					pandinLock.unlock();

					// endMyPandinStage();
				}
			} else {
				this.myView.recSysMsg("The magic doesn't take effects.");
				if (str[2].equalsIgnoreCase(myPlayerName)) {
					pandinLock.unlock();
				}
			}
		} else {
			this.myView.recSysMsg("The pandin effect could be changed.");
			// wait for those who can change pandin
		}
	}

	public void watchPlay() {
		// System.out.println("watchPlay. about to clear.");
		myView.clearProgress();
		System.out.println("All progress removed");
		// System.out.println("watchPlay. after clearProgress");
		// myView.recSysMsg("");
		myView.addProgressTo(currentPlayer.getName());
		if (currentPlayer.getName().equalsIgnoreCase(myPlayerName)) {
			receiveSignalPlayStage();
		}
	}

	public void watchPlay(String userName) {

		myView.clearProgress();
		myView.addProgressTo(userName);
		if (userName.equalsIgnoreCase(myPlayerName)) {
			receiveSignalPlayStage();
		}
	}

	public void watchPlayEnd(String userName) {
		// if the username is yourself, and if the playStageFinished == false;
		// trigger end();
		if (userName.equalsIgnoreCase(myPlayerName)) {
			// this will be called when the all Progress.finish();
			// myView.timesUp();
			myView.playStageFinished = true;
			myView.lock.unlock();
		}
		myView.recSysMsg(userName + "'s play stage ended.");
	}

	public void activatePandinChange(String msg) {
		String[] str = msg.split("\\.");
		String playerWhoChange = str[2];
		// 如果是自己，就触发操作。
		if (playerWhoChange.equalsIgnoreCase(myPlayerName)) {
			processCardInfo(str[5], str[6], str[7]);
			this.myView.recSysMsg("Do you want to change " + str[3] + "'s "
					+ str[4] + "pandin?(" + cardInfo[0] + " " + cardInfo[1]
					+ cardInfo[2] + ")");
			this.myView.pandinChangeProcessInit();
		} else {
			myView.recSysMsg(playerWhoChange + " may change Pandin now.");
		}
		// the person who will change pandin is not you
		// just set a progress bar for that person in the view
		myView.addProgressTo(playerWhoChange);
	}

	public void pandinChanged(String msg) {
		String[] str = msg.split("\\.");
		String playerWhoChange = str[2];
		processCardInfo(str[3], str[4], str[5]);
		myView.recSysMsg(playerWhoChange + " changed the pandin to "
				+ cardInfo[0] + " " + cardInfo[1] + cardInfo[2]);
	}

	public void firstRound() throws InterruptedException {
		myView.recSysMsg("GAME STARTS!");
		currentNum = 0;
		currentPlayer = playerList.get(currentNum);
		myView.recSysMsg("It's " + currentPlayer.getName() + "'s turn now.");
		myView.firstRound();
		if (currentPlayer == myPlayer) {
			myView.intoMyRound();
		}
	}

	public void askForWuXie(String theMagicName, boolean currentState) {
		String effectiveOrNot = currentState ? "effective" : "not effective";
		myView.recSysMsg("Do you want to WuXie the magic " + theMagicName + "?"
				+ "(currently " + effectiveOrNot + ")?");
		myView.wuXieProcessInit();
		myView.addProgressToAll();
	}

	public abstract void heroChosen(Hero toB);

	public abstract void quit();

	// triggered when someone played an WuXie
	public abstract void watchWuxie(String userName, String theMagicName,
			boolean effect);

	// @return false if the attack is neutralized and lost effect.
	public boolean watchAttack(String source, String target,
			String attackNature, String attackPoint, String cardName,
			String cardColor, String cardNumber) {

		// cardcolor will be useful, because renwang will ignore it.
		// this will be done in the server model.
		// also needs to do something in this model. Not trigger the
		// Hero.dodgeInit()
		myView.recSysMsg(source + " attacks " + target + ".(" + attackNature
				+ "," + attackPoint + ")");
		Hero targetHero = null;
		// if (myPlayerName.equalsIgnoreCase(source)) {
		// //prolly wrong, because dodge process should be done before attacker
		// //continue his round
		// // when we ClientModel.attack(), we locked, unlock here
		// msgLock.unlock();
		// }
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(target)) {
				targetHero = every.getCharacter();
				break;
			}
		}
		{// conditions that neutralizes attack
			if (attackNature.equalsIgnoreCase("Normal")
					&& targetHero.armorEquipped
					&& targetHero.armor.checkValidCard("TengJia")) {
				myView.recSysMsg("TengJia takes effect. Attack neutralized.");
				return false;
			}
			int colorNum = Integer.parseInt(cardColor);
			if (colorNum >= 3 && targetHero.armorEquipped
					&& targetHero.armor.checkValidCard("RengWang")) {
				myView.recSysMsg("RengWang takes effect. Attack neutralized.");
				return false;
			}
		}
		if (myPlayerName.equalsIgnoreCase(target)) {
			myView.recSysMsg("Do you want to Dodge?");
			myHero.dodgeInit();
		}
		// done in watchPlayCard();
		// else if (myPlayerName.equals(source)) {
		// myHero.playCardConfirmed();
		// }
		myView.addProgressTo(target);
		return true;
	}

	public void watchBaGua(String target) {
		if (myPlayerName.equalsIgnoreCase(target)) {
			if (myHero.armorEquipped && myHero.armor.checkValidCard("BaGua")) {
				myView.recSysMsg("Do you want to use BaGua?");
				myHero.baGuaInit();
			}
		} else {
			myView.recSysMsg(target + " may use BaGua to Dodge");
		}
		myView.addProgressTo(target);
	}

	public Lock playCardLock;

	public abstract void playCard(Card toBplayed);

	public void watchPlayCard(String username, String cardName, String color,
			String number) {
		processCardInfo(cardName, color, number);
		if (username.equalsIgnoreCase(myPlayerName)) {
			myHero.playCardConfirmed();
			playCardLock.unlock();
		} else {
			Hero targetHero = null;
			// get target hero
			for (Player every : playerList) {
				if (every.getName().equalsIgnoreCase(username)) {
					targetHero = every.getCharacter();
					break;
				}
			}
			targetHero.handCardNum--;
		}
		myView.recSysMsg(username + " plays " + cardInfo[0] + " " + cardInfo[1]
				+ cardInfo[2] + ".");
	}

	// public Lock dodgeLock;

	public abstract void dodge();

	public void watchDodge(String userName) {
		myView.recSysMsg(userName + " dodges the Attack.");
	}

	public void askForSave(String targetName, int healNumNeeded,
			String saverName) {
		myView.recSysMsg(targetName + " needs "
				+ (healNumNeeded == 1 ? "1 Heal " : healNumNeeded + " Heals ")
				+ "to survive");
		if (saverName.equalsIgnoreCase(myPlayerName)) {
			myView.recSysMsg("Do you want to play Heal(s)?");
			boolean spiritUsable = myPlayerName.equalsIgnoreCase(targetName);
			myHero.saveInit(healNumNeeded, spiritUsable);
		}
		myView.addProgressTo(saverName);
	}

	public abstract void playerDead(String targetName)
			throws InterruptedException;

	public abstract void playerRevived(String targetName);

	public abstract void recover(String userName, String helperName,
			String point);

	public void winLose(int i) {
		switch (i) {
		case 1:
			myView.recSysMsg("ZhuGong and ZhongCheng Win");
			break;
		case 2:
			myView.recSysMsg("ZhongCheng win");
			break;
		case 3:
			myView.recSysMsg("FanZei win");
			break;
		case 4:
			myView.recSysMsg("NeiJian win");
			break;
		}
		if (myPlayer.identity.whoNum == i) {
			// TODO winning animation
		} else {
			// TODO losing animation
		}

		// TODO should also stop the whole program.
		while (true) {
			try {
				Thread.sleep(100000000);
			} catch (InterruptedException e) {
				System.err.println("How the fuck can this ever be wrong.");
				e.printStackTrace();
			}
		}
	}

	// public Lock saveLock;

	public abstract void save(int healNumPlayed);

	public abstract void watchSave(String username, int savePoints);

	public void watchDiscard(String userName) {
		myView.clearProgress();
		myView.addProgressTo(userName);
		if (userName.equalsIgnoreCase(myPlayerName)) {
			receiveSignalDiscardStage();
		}
	}

	// IMPORTANT, this lock is used together with View.lock in discard phase, to
	// prevent the situation that one user get DiscardConfirm message and
	// DiscardActionEnd together. Which will lead to a new discard phase with a
	// wrong discard num required.
	public Lock discardLock;

	public abstract void discard(ArrayList<Card> toBdiscarded);

	// public void watchPlayCard(String username, String cardName, String color,
	// String number) {
	// processCardInfo(cardName, color, number);
	// if (username.equalsIgnoreCase(myPlayerName)) {
	// myHero.playCardConfirmed();
	// msgLock.unlock();
	// }
	// myView.recSysMsg(username + " plays " + cardInfo[0] + " " + cardInfo[1]
	// + cardInfo[2] + ".");
	// }

	public void watchDiscardCard(String msg) {
		// "G.DISCARDCARD." + userName + "." + cardNumber+"."+cardInfo
		String[] str = msg.split("\\.");
		String tobprint = str[2] + " discards ";
		int cardNum = Integer.parseInt(str[3]);
		tobprint += ((cardNum == 1 ? " 1 card" : " " + cardNum + " cards") + ": ");
		for (int i = 0; i < cardNum; i++) {
			processCardInfo(str[4 + i * 3], str[5 + i * 3], str[6 + i * 3]);
			tobprint += (cardInfo[0] + " " + cardInfo[1] + cardInfo[2] + ", ");
		}
		myView.recSysMsg(tobprint);
		if (str[2].equalsIgnoreCase(myPlayerName)) {
			myHero.discardCardConfirmed();
			discardLock.unlock();
		} else {
			Hero targetHero = null;
			for (Player every : playerList) {
				if (every.getName().equalsIgnoreCase(str[2])) {
					targetHero = every.getCharacter();
					break;
				}
			}
			targetHero.handCardNum -= cardNum;
		}
	}

	public void watchDiscardEnd(String userName) {
		// if the username is yourself, and if the discardStageFinished ==
		// false;
		// trigger end();
		if (userName.equalsIgnoreCase(myPlayerName)) {
			myView.discardStageFinished = true;
			myView.lock.unlock();
		}
		myView.recSysMsg(userName + "'s Discard Stage ended.");
	}

	public abstract void multiAttack(ArrayList<Hero> targets, Damage damage,
			String string);

	// public Lock moveLock;

	public abstract void move(Square targetSquare);

	public void watchMove(String userName, int x, int y) {
		Square targetSquare = myView.myBattleField.squares[x - 1][y - 1];
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(userName)) {
				every.getCharacter().moveConfirmed(targetSquare);
				break;
			}
		}
		// if (userName.equalsIgnoreCase(myPlayerName)) {
		// moveLock.unlock();
		// }
		myView.recSysMsg(userName + " moves to (" + x + ", " + y + ").");
	}

	// public Lock drinkSpiritLock;

	public abstract void drinkSpirit();

	public void watchDrinkSpirit(String userName) {
		myView.recSysMsg(userName + " drinks spirit.");
		if (userName.equalsIgnoreCase(myPlayerName)) {
			// drinkSpiritLock.unlock();
			myHero.damage.setPoint(myHero.damage.getPoint() + 1);
		}
	}

	// public Lock wuXieLock;

	public abstract void wuXie();

	// public Lock binLiangLock;

	public abstract void binLiang(String targetName, String cardInfo);

	public void watchBinLiang(String msg) {
		// "G.BINLIANG."+userName+"."+ targetName+"."+cardInfo

		// TODO be careful, the msg also includes cardInfo(name,color,number),
		// this is not used now, but when JaXv.LianMu is in Game, this is used
		// here.
		// UPDATE, the right idea is, do all this in Server.java when receiving
		// messages
		// UPDATE, this new idea won't work, because server will broadcast all
		// players at the same time:
		// 1. if mutliple passive skills are triggered, will cause sequence
		// chaos
		// 2. the Client who play card will unlock to continue playCardStage,
		// while passive skills of other players are triggered, will cause
		// chaos.
		// NEW IDEA. not only is active actions(Attack) is within hero's methods
		// to trigger skills. Also passive actions(watchAttack) can also be in
		// hero's methods to trigger skills.
		String str[] = msg.split("\\.");
		myView.recSysMsg(str[2] + " uses BinLiang to " + str[3]);
		Hero targetHero = null;
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(str[3])) {
				targetHero = every.getCharacter();
				break;
			}
		}
		targetHero.ifBinLiang = true;
		targetHero.BinLiangCard = Card.createCard(str[4],
				Integer.parseInt(str[5]), Integer.parseInt(str[6]));
		// if (str[2].equalsIgnoreCase(myPlayerName)) {
		// binLiangLock.unlock();
		// }
	}

	// public Lock leBuLock;

	public abstract void leBu(String targetName, String cardInfo);

	public void watchLeBu(String msg) {
		// "G.LEBU."+userName+"."+targetName+"."+cardInfo
		// same with watchBinLiang()
		String str[] = msg.split("\\.");
		myView.recSysMsg(str[2] + " uses LeBu to " + str[3]);
		Hero targetHero = null;
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(str[3])) {
				targetHero = every.getCharacter();
				break;
			}
		}
		targetHero.ifLeBu = true;
		targetHero.LeBuCard = Card.createCard(str[4], Integer.parseInt(str[5]),
				Integer.parseInt(str[6]));
		// if (str[2].equalsIgnoreCase(myPlayerName)) {
		// leBuLock.unlock();
		// }
	}

	// public Lock shanDianLock;

	public abstract void shanDian(String cardInfo);

	public void watchShanDian(String msg) {
		// "G.SHANDIAN."+ userName +"."+str[2]+"."+str[3]+"."+str[4]
		String str[] = msg.split("\\.");
		myView.recSysMsg(str[2] + " uses ShanDian to self.");
		Hero targetHero = null;
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(str[2])) {
				targetHero = every.getCharacter();
				break;
			}
		}
		targetHero.ifShanDian = true;
		targetHero.ShanDianCard = Card.createCard(str[3],
				Integer.parseInt(str[4]), Integer.parseInt(str[5]));
		// if (str[2].equalsIgnoreCase(myPlayerName)) {
		// shanDianLock.unlock();
		// }
	}

	// public Lock guoHeLock;

	public abstract void guoHe(String name, String string);

	public void watchGuoHe(String msg) {
		String[] str = msg.split("\\.");
		// "G.GUOHE."+ sourceName +"."+ targetName+"."+ cardInfo
		if (myPlayerName.equalsIgnoreCase(str[2])) {
			// guoHeLock.unlock();
			myView.guoHeConfirmInit(str[3]);
		} else {
			// do nothing
		}
		myView.addProgressTo(str[2]);
	}

	public void watchShunShou(String msg) {
		String[] str = msg.split("\\.");
		// "G.SHUNSHOU." + userName + "." + targetName+"."+ cardInfo
		if (myPlayerName.equalsIgnoreCase(str[2])) {
			// msgLock.unlock();
			myView.shunShouConfirmInit(str[3]);
		}
		// else do nothing
		myView.addProgressTo(str[2]);
	}

	// also need a ServerModel.watchGuoHeConfirmed(msg)
	public void watchGuoHeConfirmed(String msg) {
		// "G.GUOHECONFIRM."+userName+"."+targetName+"."+targetCard.toString()
		String[] str = msg.split("\\.");
		Card targetCard = Card.createCard(str[4], Integer.parseInt(str[5]),
				Integer.parseInt(str[6]));
		processCardInfo(str[4], str[5], str[6]);
		if (myPlayerName.equalsIgnoreCase(str[3])) {
			boolean findit = false;
			for (Card every : myHero.handCards) {
				if (every.equalsCard(targetCard)) {
					findit = true;
					// myHero.handCards.remove(every);
					// myHero.handCardNum -= 1;
					// myView.removeMyCard(every);
					myHero.loseCard(every);
					break;
				}
			}
			if (!findit) {// the card is in the equipment card.

				if (myHero.weaponEquipped
						&& myHero.weapon.equalsCard(targetCard)) {
					myHero.loseWeapon();
					findit = true;
				}
				if (!findit && myHero.armorEquipped
						&& myHero.armor.equalsCard(targetCard)) {
					myHero.loseArmor();
					findit = true;
				}
				if (!findit && myHero.plusHorseEquipped
						&& myHero.plusHorse.equalsCard(targetCard)) {
					myHero.losePlusHorse();
					findit = true;
				}
				if (!findit && myHero.minusHorseEquipped
						&& myHero.minusHorse.equalsCard(targetCard)) {
					myHero.loseMinusHorse();
					findit = true;
				}
			}

			myView.recSysMsg("You lose " + cardInfo[0] + " " + cardInfo[1]
					+ cardInfo[2]);
		} else {// it's not my player
			boolean findit = false;
			Hero targetHero = null;
			// find the target hero
			for (Player every : playerList) {
				if (every.getName().equalsIgnoreCase(str[3])) {
					targetHero = every.getCharacter();
					break;
				}
			}
			if (targetHero.weaponEquipped
					&& targetHero.weapon.equalsCard(targetCard)) {
				targetHero.weaponEquipped = false;
				targetHero.weapon = null;
				findit = true;
			}
			if (!findit && targetHero.armorEquipped
					&& targetHero.armor.equalsCard(targetCard)) {
				targetHero.armorEquipped = false;
				targetHero.armor = null;
				findit = true;

			}
			if (!findit && targetHero.plusHorseEquipped
					&& targetHero.plusHorse.equalsCard(targetCard)) {
				targetHero.plusHorseEquipped = false;
				targetHero.plusHorse = null;
				findit = true;

			}
			if (!findit && targetHero.minusHorseEquipped
					&& targetHero.minusHorse.equals(targetCard)) {
				targetHero.minusHorseEquipped = false;
				targetHero.minusHorse = null;
				findit = true;

			}
			if (!findit) {
				targetHero.handCardNum -= 1;
				myView.recSysMsg(str[3] + " loses one handCard.");
			} else {
				myView.recSysMsg(str[3] + " loses " + cardInfo[0] + " "
						+ cardInfo[1] + cardInfo[2] + ".");
			}

		}
		// if (str[2].equalsIgnoreCase(myPlayerName)) {
		// guoHeLock.unlock();
		// }
	}

	// also need a ServerModel.watchShunShouConfirm(msg)
	public void watchShunShouConfirmed(String msg) {
		// "G.SHUNSHOUCONFIRM."+sourceName+"."+targetName+"."+targetCard.toString()
		String[] str = msg.split("\\.");
		Card targetCard = Card.createCard(str[4], Integer.parseInt(str[5]),
				Integer.parseInt(str[6]));
		System.out.println("WatchShunShouConfirm: targetCard " + targetCard);
		processCardInfo(str[4], str[5], str[6]);
		// the target will lose this card
		if (myPlayerName.equalsIgnoreCase(str[3])) {
			boolean findit = false;
			for (Card every : myHero.handCards) {
				System.out.println("WatchShunShouConfirm: every " + every);
				if (every.equalsCard(targetCard)) {
					findit = true;
					// myHero.handCards.remove(every);
					// myHero.handCardNum -= 1;
					// myView.removeMyCard(every);
					myHero.loseCard(every);
					break;
				}
			}
			if (!findit) {// the card is in the equipment card.

				if (myHero.weaponEquipped
						&& myHero.weapon.equalsCard(targetCard)) {
					myHero.loseWeapon();
					findit = true;
				}
				if (!findit && myHero.armorEquipped
						&& myHero.armor.equalsCard(targetCard)) {
					myHero.loseArmor();
					findit = true;
				}
				if (!findit && myHero.plusHorseEquipped
						&& myHero.plusHorse.equalsCard(targetCard)) {
					myHero.losePlusHorse();
					findit = true;
				}
				if (!findit && myHero.minusHorseEquipped
						&& myHero.minusHorse.equalsCard(targetCard)) {
					myHero.loseMinusHorse();
					findit = true;
				}
			}

			myView.recSysMsg("You lose " + cardInfo[0] + " " + cardInfo[1]
					+ cardInfo[2]);
		} else {// it's not my player
			boolean findit = false;
			Hero targetHero = null;
			// find the target hero
			for (Player every : playerList) {
				if (every.getName().equalsIgnoreCase(str[3])) {
					targetHero = every.getCharacter();
					break;
				}
			}
			if (targetHero.weaponEquipped
					&& targetHero.weapon.equalsCard(targetCard)) {
				targetHero.weaponEquipped = false;
				targetHero.weapon = null;
				findit = true;
			}
			if (!findit && targetHero.armorEquipped
					&& targetHero.armor.equalsCard(targetCard)) {
				targetHero.armorEquipped = false;
				targetHero.armor = null;
				findit = true;

			}
			if (!findit && targetHero.plusHorseEquipped
					&& targetHero.plusHorse.equalsCard(targetCard)) {
				targetHero.plusHorseEquipped = false;
				targetHero.plusHorse = null;
				findit = true;

			}
			if (!findit && targetHero.minusHorseEquipped
					&& targetHero.minusHorse.equalsCard(targetCard)) {
				targetHero.minusHorseEquipped = false;
				targetHero.minusHorse = null;
				findit = true;

			}
			if (!findit) {
				targetHero.handCardNum -= 1;
				myView.recSysMsg(str[3] + " loses one handCard.");
			} else {
				myView.recSysMsg(str[3] + " loses " + cardInfo[0] + " "
						+ cardInfo[1] + cardInfo[2] + ".");
			}

		}
		// the source will get this card
		if (myPlayerName.equalsIgnoreCase(str[2])) {
			myHero.getCard(targetCard);
			myView.recSysMsg("You get " + cardInfo[0] + " " + cardInfo[1]
					+ cardInfo[2]);
		} else {// it's not my player
			// find the target hero
			Hero targetHero = null;
			for (Player every : playerList) {
				if (every.getName().equalsIgnoreCase(str[2])) {
					targetHero = every.getCharacter();
					break;
				}
			}
			targetHero.handCardNum += 1;
			myView.recSysMsg(str[2] + " gets this card");
		}
		// if (str[2].equalsIgnoreCase(myPlayerName)) {
		// shunShouLock.unlock();
		// }
	}

	public abstract void guoHeConfirm(String targetName, Card targetCard);

	public abstract void shunShouConfirm(String targetName, Card targetCard);

	// public Lock healLock;

	public abstract void heal(Card healCard, Hero target);

	public void watchHeal(String targetName, String helperName, int points) {
		Player targetPlayer = null;
		// find the target hero
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(targetName)) {
				targetPlayer = every;
				break;
			}
		}
		Hero targetHero = targetPlayer.getCharacter();
		int actualHealPoints = targetHero.recoverHealth(points);
		if (actualHealPoints == 0) {
			return;
		}
		if (helperName != null) {
			myView.recSysMsg(helperName + " heals " + targetName + " "
					+ actualHealPoints + " hp");
		} else {
			myView.recSysMsg(targetName + " heals " + actualHealPoints + " hp");
		}
	}

	// public Lock shunShouLock;

	public abstract void shunShou(String name, String string);

	// @undeprecated, IMPORTANT, what locks are needed in our Model? Only those
	// ones that if we don't lock, we won't be able to control the flow of our
	// View.
	// This means we need to lock actions right before another subsequent
	// action.
	// For actions triggered by Ok(). The View.lock will provide a block, thus
	// don't need us to add one more lock.
	// for all the answer actions, answer lock is provided by server

	// deprecated, all locks and blocks are moved into Server side.
	ArrayList<Lock> lockList;

	private void initAllLocks() {
		lockList = new ArrayList<Lock>();
		{
			// healLock = new Lock();
			// lockList.add(healLock);
			playCardLock = new Lock();
			lockList.add(playCardLock);
			drawCardLock = new Lock();
			lockList.add(drawCardLock);
			pandinLock = new Lock();
			lockList.add(pandinLock);
			// attackLock = new Lock();
			// lockList.add(attackLock);
			// dodgeLock = new Lock();
			// lockList.add(dodgeLock);
			// saveLock = new Lock();
			// lockList.add(saveLock);
			// binLiangLock = new Lock();
			// lockList.add(binLiangLock);
			// leBuLock = new Lock();
			// lockList.add(leBuLock);
			// shanDianLock = new Lock();
			// lockList.add(shanDianLock);
			// guoHeLock = new Lock();
			// lockList.add(guoHeLock);
			// shunShouLock = new Lock();
			// lockList.add(shunShouLock);
			// drinkSpiritLock = new Lock();
			// lockList.add(drinkSpiritLock);
			// wuXieLock = new Lock();
			// lockList.add(wuXieLock);
			// ciXiongResponseLock = new Lock();
			// lockList.add(ciXiongResponseLock);
			// moveLock = new Lock();
			// lockList.add(moveLock);
			signalPlayStageLock = new Lock();
			lockList.add(signalPlayStageLock);
			signalDiscardStageLock = new Lock();
			lockList.add(signalDiscardStageLock);
			discardLock = new Lock();
			lockList.add(discardLock);
		}
	}

	public void releaseAllLocks() {
		for (Lock every : lockList) {
			every.unlock();
		}
	}

	public abstract void sayNoToAction(String action);

	public abstract void signalRoundChange();

	public abstract void signalDiscardStageEnd();

	public abstract void equipHorseMinus(Card equip);

	public void watchLoseHorseMinus(String userName) {
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(userName)) {
				every.getCharacter().loseMinusHorse();
				break;
			}
		}
		myView.recSysMsg(userName + " loses his minusHorse");
	}

	public abstract void equipArmor(Card equip);

	public void watchLoseArmor(String userName) {
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(userName)) {
				every.getCharacter().loseArmor();
				break;
			}
		}
		myView.recSysMsg(userName + " loses his Armor");

	}

	public abstract void equipWeapon(Card equip);

	public void watchLoseWeapon(String userName) {

		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(userName)) {
				every.getCharacter().loseWeapon();
				break;
			}
		}
		myView.recSysMsg(userName + " loses his weapon");

	}

	public abstract void equipHorsePlus(Card equip);

	public void watchLoseHorsePlus(String userName) {
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(userName)) {
				every.getCharacter().losePlusHorse();
				break;
			}
		}
		myView.recSysMsg(userName + " loses his plusHorse");

	}

	public void watchEquipHorseMinus(String username, String cardName,
			String color, String number) {
		// if(username.equalsIgnoreCase(myPlayerName)){
		HorseMinus toB = (HorseMinus) Card.createCard(cardName,
				Integer.parseInt(color), Integer.parseInt(number));
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(username)) {
				every.getCharacter().equipMinusHorse(toB);
				break;
			}
		}
		// }
		processCardInfo(cardName, color, number);
		myView.recSysMsg(username + " equips " + cardInfo[0] + " "
				+ cardInfo[1] + cardInfo[2] + ".");
	}

	public void watchEquipArmor(String username, String cardName, String color,
			String number) {
		Armor toB = (Armor) Card.createCard(cardName, Integer.parseInt(color),
				Integer.parseInt(number));
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(username)) {
				every.getCharacter().equipArmor(toB);
				break;
			}
		}
		processCardInfo(cardName, color, number);
		myView.recSysMsg(username + " equips " + cardInfo[0] + " "
				+ cardInfo[1] + cardInfo[2] + ".");
	}

	public void watchEquipWeapon(String username, String cardName,
			String color, String number) {
		Weapon toB = (Weapon) Card.createCard(cardName,
				Integer.parseInt(color), Integer.parseInt(number));
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(username)) {
				every.getCharacter().equipWeapon(toB);
				break;
			}
		}
		processCardInfo(cardName, color, number);
		myView.recSysMsg(username + " equips " + cardInfo[0] + " "
				+ cardInfo[1] + cardInfo[2] + ".");
	}

	public void watchEquipHorsePlus(String username, String cardName,
			String color, String number) {
		HorsePlus toB = (HorsePlus) Card.createCard(cardName,
				Integer.parseInt(color), Integer.parseInt(number));
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(username)) {
				every.getCharacter().equipPlusHorse(toB);
				break;
			}
		}
		processCardInfo(cardName, color, number);
		myView.recSysMsg(username + " equips " + cardInfo[0] + " "
				+ cardInfo[1] + cardInfo[2] + ".");
	}

	public abstract void wuGu();

	public void watchWuGuBeforeWuXie(String msg) {
		// "G.WUGU." + answeree + "."+ cardNum+"."+ cardsInfo;
		String str[] = msg.split("\\.");
		myView.recSysMsg("WuGu process: it is " + str[2]
				+ "'s turn to pick card now.");
	}

	public void watchWuGu(String msg) {
		// "G.WUGU." + answeree + "."+ cardNum+"."+ cardsInfo;
		String str[] = msg.split("\\.");
		if (myPlayerName.equalsIgnoreCase(str[2])) {
			// parsing all the cards
			int cardNum = Integer.parseInt(str[3]);
			ArrayList<Card> cards = new ArrayList<Card>();
			for (int i = 0; i < cardNum; i++) {
				Card newCard = Card.createCard(str[4 + i * 3],
						Integer.parseInt(str[5 + i * 3]),
						Integer.parseInt(str[6 + i * 3]));
				cards.add(newCard);
			}
			myView.wuGuConfirmInit(cardNum, cards);
		}
	}

	public abstract void watchWuGuConfirm(String userName, Card thePickedCard);

	public abstract void wuGuConfirm(Card targetCard);

	public abstract void jueDou(String targetName);

	public void watchJueDou(String userName, String targetName) {
		myView.recSysMsg(userName + " uses JueDou toward " + targetName);
	}

	public void watchJueDouConfirm(String userName) {
		if (myPlayerName.equalsIgnoreCase(userName)) {
			myView.recSysMsg("Do you want to accept the Challenge?");
			myHero.jueDouConfirmInit();
		} else {
			myView.recSysMsg("JueDou process: it's " + userName
					+ "'s turn to play Attack now");
		}
		myView.addProgressTo(userName);
	}

	public abstract void jueDouConfirm();

	public abstract void wuZhong();

	public void watchWuZhong(String userName) {
		myView.recSysMsg(userName + " uses WuZhong and will draw 2 cards.");
	}

	public abstract void wanJian();

	public void watchWanJianBeforeWuXie(String userName) {
		myView.recSysMsg("WanJian process: it is " + userName
				+ "'s turn to play Dodge now.");
	}

	public void watchWanJian(String answeree) {
		if (myPlayerName.equalsIgnoreCase(answeree)) {
			myView.recSysMsg("Do you want to Dodge the WanJian?");
			myHero.wanJianConfirmInit();
		} else {
			myView.recSysMsg("It's " + answeree + "'s turn to dodge WanJian");
		}
		myView.addProgressTo(answeree);
	}

	public abstract void wanJianConfirm();

	public void watchWanJianConfirm(String userName) {
		myView.recSysMsg(userName + " dodged the WanJian");
	}

	public void watchWanJianTengJia(String userName) {
		myView.recSysMsg(userName
				+ "'s TengJia takes effect, immuned to WanJian.");
	}

	public abstract void nanMan();

	public void watchNanManBeforeWuXie(String userName) {
		myView.recSysMsg("NanMan process: it is " + userName
				+ "'s turn to play Attack now.");
	}

	public void watchNanMan(String answeree) {
		if (myPlayerName.equalsIgnoreCase(answeree)) {
			myView.recSysMsg("Do you want to Attack the NanMan?");
			myHero.nanManConfirmInit();
		} else {
			myView.recSysMsg("It's " + answeree + "'s turn to attack NanMan");
		}
		myView.addProgressTo(answeree);
	}

	public abstract void nanManConfirm();

	public void watchNanManConfirm(String userName) {
		myView.recSysMsg(userName + " attacked the NanMan");
	}

	public void watchNanManTengJia(String userName) {
		myView.recSysMsg(userName
				+ "'s TengJia takes effect, immuned to NanMan.");
	}

	public abstract void taoYuan();

	public void watchTaoYuanBeforeWuXie(String userName) {
		myView.recSysMsg("TaoYuan process: it is " + userName
				+ "'s turn to heal from TaoYuan now.");
	}

	public abstract void jieDao(String targetName);

	public void watchJieDaoInit(String userName, String answeree) {
		if (myPlayerName.equalsIgnoreCase(userName)) {
			myView.jieDaoProcessInit2(answeree);
		}
		// else do nothing
		myView.addProgressTo(userName);
	}

	public abstract void jieDao2(String name);

	public void watchJieDaoBeforeWuXie(String jieDaoSource,
			String jieDaoTarget1, String jieDaoTarget2) {
		myView.recSysMsg(jieDaoSource + " uses JieDao and orders "
				+ jieDaoTarget1 + " to attack " + jieDaoTarget2
				+ " or to give his weapon to " + jieDaoSource);
	}

	public void watchJieDao(String jieDaoSource, String jieDaoTarget1,
			String jieDaoTarget2) {
		if (myPlayerName.equalsIgnoreCase(jieDaoTarget1)) {
			myView.jieDaoConfirmProcessInit(jieDaoSource, jieDaoTarget2);
		}
		myView.addProgressTo(jieDaoTarget1);
	}

	public void watchJieDaoSuccess(String userName, String cardName,
			String color, String number) {
		processCardInfo(cardName, color, number);
		myView.recSysMsg(userName + " gets card: " + cardInfo[0] + " "
				+ cardInfo[1] + cardInfo[2]);
		if (userName.equalsIgnoreCase(myPlayerName)) {
			myHero.getCard(Card.createCard(cardName, Integer.parseInt(color),
					Integer.parseInt(number)));
		} else {
			for (Player every : playerList) {
				if (every.getName().equalsIgnoreCase(userName)) {
					every.getCharacter().handCardNum += 1;
					break;
				}
			}
		}
	}

	public abstract void jieDaoConfirm(String targetName, Damage damage,
			String cardInfo);

	public abstract void nanManTengJia();

	public abstract void wanJianTengJia();

	public abstract void tieSuo(Card tieSuoCard, ArrayList<Hero> targets);

	public void watchTieSuoBeforeWuXie(String currentTargetName) {
		myView.recSysMsg(currentTargetName
				+ " is about to be Linked now by TieSuo");
	}

	public void watchTieSuo(String currentTargetName, Card tieSuoCard) {
		for (Player every : playerList) {
			if (every.getName().equalsIgnoreCase(currentTargetName)) {
				Hero targetHero = every.getCharacter();
				if (targetHero.ifLinked()) {
					every.getCharacter().link = null;
				} else {
					every.getCharacter().link = (TieSuo) tieSuoCard;
				}
				break;
			}
		}
	}

	public abstract void huoGong(Card huoGongCard, String name);

	public void watchHuoGong(String starterName, String answeree) {
		myView.recSysMsg(starterName + " uses HuoGong on " + answeree + ".");
		if (myPlayerName.equalsIgnoreCase(answeree)) {
			myView.huoGongConfirm1Init(starterName);
		}
		myView.addProgressTo(answeree);
	}

	public abstract void huoGongConfirm1(Card huoGong1Card,
			String huoGongStarterName);

	public void watchHuoGong1(String toBSplitted) {
		// "G.HUOGONGCONFIRM1."+ userName+ "."+cardInfo
		String str[] = toBSplitted.split("\\.");
		processCardInfo(str[3], str[4], str[5]);
		myView.recSysMsg(str[2] + " showed the card " + cardInfo[0] + " "
				+ cardInfo[1] + cardInfo[2] + ".");
		if (str[6].equalsIgnoreCase(myPlayerName)) {
			myView.huoGongConfirm2Init(str[4], cardInfo[1], str[2]);
		}
		myView.addProgressTo(str[6]);
	}

	public abstract void huoGongConfirm2(Card huoGong2Card, String targetName);

	public void watchHuoGong2(String msg) {
		// String str[] = msg.split("\\.");
		myView.recSysMsg("HuoGong takes effect");
	}

	public void watchHuoGongFail() {
		myView.recSysMsg("HuoGong is failed.");
	}

	public abstract void baGua();

	public void watchBaGuaConfirm(String msg) {
		// "G.BAGUACONFIRM."+ userName+"."+ answered+"."+baGuaPandin
		String str[] = msg.split("\\.");
		processCardInfo(str[4], str[5], str[6]);
		String takesEffect = "";
		if (Boolean.parseBoolean(str[3])) {
			takesEffect = " takes effect";
		} else {
			takesEffect = " not takes effect";
		}
		myView.recSysMsg(str[2] + "'s BaGua" + takesEffect
				+ ". The Pandin card is " + cardInfo[0] + " " + cardInfo[1]
				+ cardInfo[2]);
	}

	public abstract void zhuQue();

	public void watchZhuQue(String userName) {
		myView.recSysMsg(userName+ " uses ZhuQue, Attack damage will be in Fire nature");
		myHero.damage.setNature(Damage.Nature.fire);
		myView.unlockWeaponSkill();
	}
}
